package util

import java.lang.annotation.Annotation
import java.lang.reflect.{Proxy, Method, InvocationHandler}

/**
 * <p>
 * Created by IntelliJ IDEA.
 * User: zslajchrt
 * Date: Feb 15, 2011
 * Time: 6:48:43 PM
 */

object AnnotationWrapper {
  def apply[A <: Annotation](annotation: A): A = {
    val atype = annotation.annotationType
    var values: Map[String, AnyRef] = Map()
    atype.getDeclaredMethods.foreach((m) => {
      val value = m.invoke(annotation)
      values += (m.getName -> value)
    })
    val proxy = Proxy.newProxyInstance(Thread.currentThread.getContextClassLoader,
      Array(atype), new AnnotationWrapper(values, annotation.toString, atype, annotation.hashCode))
    proxy.asInstanceOf[A]
  }
}
@serializable
class AnnotationWrapper[A <: Annotation](values: Map[String, AnyRef], stringExpr: String,
                                         annotType: Class[A], hash: Int) extends InvocationHandler {
  def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]) = {
    if (values.contains(method.getName) && method.getParameterTypes.length == 0) {
      values(method.getName)
    } else {
      method.getName match {
        case "toString" => {
          stringExpr
        }
        case "hashCode" => {
          hash.asInstanceOf[AnyRef]
        }
        case "annotationType" => {
          annotType
        }
        case "equals" => {
          val other = args(0)
          val result = if (other == null)
            false
          else if (other eq proxy)
            true
          else if (!annotType.isInstance(other))
            false
          else {
            values.forall((pair) => {
              val (name, value) = pair
              annotType.getMethod(name).invoke(other) == value
            })
          }

          result.asInstanceOf[AnyRef]
        }
        case _ => method.invoke(this, args)
      }
    }
  }
}